package cn.shop.front.controller;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.session.SaSessionCustomUtil;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.shop.base.UserInfoForSession;
import cn.shop.front.controller.wx.utils.Constants;
import cn.shop.front.entity.req.ResetPasswordReq;
import cn.shop.front.service.FrontUserService;
import cn.shop.thiredparty.entity.SmsSendResult;
import cn.shop.thiredparty.entity.SmsSendStatus;
import cn.shop.thiredparty.service.ThiredpartyService;
import cn.shop.transcation.frontUserModule.FrontUser;
import cn.shop.transcation.login.BindWechartReq;
import cn.shop.transcation.login.LoginInfoReq;
import cn.shop.utils.ApiResult;
import cn.shop.utils.Putif;
import cn.shop.utils.constant.Constant;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.Date;
import java.util.Map;

import static cn.shop.utils.ApiResult.fail;
import static cn.shop.utils.ApiResult.result;
import static cn.shop.utils.constant.SMSVerificationCode.ADV_CODE;
import static cn.shop.utils.constant.SMSVerificationCode.RESET_PWD;

/**
 * @author : 李一涵
 * @since : 2022-8-16 15:31
 */
@Slf4j
@RestController
@RequestMapping("/api/v1/frontUser")
public class LoginController {

    /**
     * 服务对象
     */
    @Resource
    private FrontUserService frontUserService;

    @Resource
    private ThiredpartyService thiredpartyService;

    private static final int EXPIRE_MIN = 5;

    /**
     * 判断是否高级账号
     *
     * @param account 账号名
     * @return 返回结果
     */
    @GetMapping("/isadv")
    public ApiResult<Map<String, Object>> isAdvUser(String account) {
        FrontUser user = frontUserService.getOne(Wrappers.<FrontUser>lambdaQuery().eq(FrontUser::getAccount, account));
        Map<String, Object> data = Putif.start()
                .anyway("isAdv", user != null && user.getAdvanced())
                .get();
        return ApiResult.success(data);
    }

    /**
     * 高级账号发送验证码
     *
     * @param account 账号名
     * @return 返回结果
     */
    @GetMapping("/advcode")
    public ApiResult<Map<String, Object>> sendAdvCode(String account) {
        FrontUser user = frontUserService.getOne(Wrappers.<FrontUser>lambdaQuery().eq(FrontUser::getAccount, account));
        if (StrUtil.isBlank(user.getPhone())) {
            return ApiResult.fail("该账号未绑定手机");
        }
        String code = RandomUtil.randomNumbers(6);
        SmsSendResult result = thiredpartyService.sendSms(code, EXPIRE_MIN, user.getPhone());
        if (result != null && result.getStatus() == SmsSendStatus.OK) {
            String key = Constant.SMS_CODE + ADV_CODE + user.getPhone();
            SaSession session = SaSessionCustomUtil.getSessionById(key);
            session.set("code", code);
            session.updateTimeout(EXPIRE_MIN * 60);
            return ApiResult.success("验证码发送成功");
        }
        else {
            return ApiResult.fail("验证码发送失败");
        }
    }

    /**
     * 登录
     *
     * @param loginInfoReq 存放账号和密码
     * @return cn.shop.utils.ApiResult<java.util.Map < java.lang.String, java.lang.Object>> 返回sessionid
     * @author jzw
     * @since 2021/11/2 14:11
     */
    @PostMapping("login")
    public ApiResult<Map<String, Object>> login(@Validated @RequestBody LoginInfoReq loginInfoReq, @RequestHeader(required = false, name = "Captcha-Token") String captchaToken) {
        Assert.notBlank(captchaToken, "captchaToken 不能为空");
        SaSession saSession = SaSessionCustomUtil.getSessionById(captchaToken, false);
        Assert.notNull(saSession, "验证码已失效");
        Object captchaSessionKey = saSession.get(Constant.CAPTCHA_SESSION_KEY);
        Assert.notNull(captchaSessionKey, "验证码已失效");
        Assert.isTrue(loginInfoReq.getCode().equalsIgnoreCase((String) captchaSessionKey), "验证码不正确");
        saSession.clear();
        LambdaQueryWrapper<FrontUser> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(FrontUser::getAccount, loginInfoReq.getAccount());
        FrontUser frontUser = frontUserService.getOne(wrapper);
        if (frontUser == null) {
            return ApiResult.fail("账户或密码错误");
        }
        if (frontUser.getRisk()) {
            return fail("账号已被禁止登录，请联系客服");
        }
        if (frontUser.getAdvanced()) {
            String key = Constant.SMS_CODE + ADV_CODE + frontUser.getPhone();
            SaSession session = SaSessionCustomUtil.getSessionById(key);
            Assert.notNull(session, "验证码已失效");
            String c = session.getString("code");
            if (StrUtil.isBlank(c) || !c.equals(loginInfoReq.getAdvcode())) {
                return fail("验证码不正确");
            }
            session.clear();
        }
        String password = frontUser.getPassword();
        Assert.notBlank(password, "密码不能为空");
        boolean b = DigestUtil.bcryptCheck(loginInfoReq.getPassword(), password);
        if (b) {
            Long frontUserId = frontUser.getId();
            StpUtil.login(frontUserId);
            String account = frontUser.getAccount();
            String name = frontUser.getName();
            StpUtil.getSession().set(Constant.USER, new UserInfoForSession(frontUserId, account, name));
            Dict dict = Dict.create();
            dict.set("token", StpUtil.getTokenValue()).set("name", name).set("account", account).set("balance", frontUser.getBalance());
            // 2023/3/21 更新最后登录时间
            frontUser.setLastLoginTime(DateUtil.toLocalDateTime(new Date()));
            frontUserService.updateById(frontUser);
            return ApiResult.success(dict);
        }
        return ApiResult.fail("账号或密码错误");
    }

    /**
     * 获取验证码
     *
     * @author jzw
     * @since 2022/1/7 17:33
     */
    @GetMapping("verificationCode")
    public ApiResult<Dict> verificationCode(@RequestParam(required = false) String token) {
        return ApiResult.success(frontUserService.verificationCode(token));
    }

    /**
     * 登出
     *
     * @return cn.shop.utils.ApiResult<java.util.Map < java.lang.String, java.lang.Object>>
     * @author jzw
     * @since 2021/11/2 14:11
     */
    @GetMapping("logout")
    public ApiResult<Map<String, Object>> logout() {
        StpUtil.logout();
        return ApiResult.success();
    }

    /**
     * 通过手机号获取验证码
     *
     * @param map 手机号
     * @return 验证码
     */
    @PostMapping("/sendcode")
    public ApiResult<Map<String, Object>> smsVerification(@RequestBody Map<String, String> map) {
        FrontUser user = frontUserService.getOne(Wrappers.<FrontUser>lambdaQuery().eq(FrontUser::getAccount, map.get("account")));
        if (user == null || StrUtil.isBlank(user.getPhone())) {
            return ApiResult.fail("该用户并未绑定手机号，请联系客服");
        }
        String code = RandomUtil.randomNumbers(6);
        SmsSendResult result = thiredpartyService.sendSms(code, EXPIRE_MIN, user.getPhone());
        if (result != null && result.getStatus() == SmsSendStatus.OK) {
            String key = Constant.SMS_CODE + RESET_PWD + user.getAccount();
            SaSession session = SaSessionCustomUtil.getSessionById(key);
            session.set("code", code);
            session.updateTimeout(EXPIRE_MIN * 60);
            return ApiResult.success("验证码发送成功");
        }
        else {
            return ApiResult.fail("验证码发送失败");
        }
    }

    /**
     * 重置登录密码
     *
     * @param req 登录密码
     * @return 返回结果
     */
    @PutMapping("/resetPassword")
    public ApiResult<Map<String, Object>> resetLoginPassword(@Validated @RequestBody ResetPasswordReq req) {
        FrontUser user = frontUserService.getOne(Wrappers.<FrontUser>lambdaQuery().eq(FrontUser::getAccount, req.getAccount()));
        if (user == null) {
            return ApiResult.fail();
        }
        String key = Constant.SMS_CODE + RESET_PWD + user.getAccount();
        SaSession session = SaSessionCustomUtil.getSessionById(key);
        Assert.notNull(session, "验证码已失效");
        String c = session.getString("code");
        log.info("获取缓存验证码：key={}，code={}，session={}, 前端code={}", key, c, session, user.getCode());
        if (StrUtil.isBlank(c) || !c.equals(req.getCode())) {
            return fail("验证码不正确");
        }
        session.clear();
        return result(frontUserService.updateLoginPassword(user.getId(), req.getPassword()));
    }

    @PostMapping("/autologin")
    public ApiResult<Map<String, Object>> loginByOpenId(@RequestBody LoginInfoReq loginInfoReq) {
        Assert.notNull(loginInfoReq, "参数错误");
        Assert.notNull(loginInfoReq.getOpenId(), "openId不能为空");
        LambdaQueryWrapper<FrontUser> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(FrontUser::getAccount, loginInfoReq.getAccount());
        FrontUser frontUser = frontUserService.lambdaQuery()
                .eq(FrontUser::getOpenId, loginInfoReq.getOpenId())
                .one();
        if (frontUser == null) {
            return ApiResult.fail("用户未绑定微信");
        }
        Long frontUserId = frontUser.getId();
        StpUtil.login(frontUserId);
        String account = frontUser.getAccount();
        String name = frontUser.getName();
        StpUtil.getSession().set(Constant.USER, new UserInfoForSession(frontUserId, account, name));
        Dict dict = Dict.create();
        dict.set("token", StpUtil.getTokenValue()).set("name", name).set("account", account).set("balance", frontUser.getBalance());
        // 2023/3/21 更新最后登录时间
        frontUser.setLastLoginTime(DateUtil.toLocalDateTime(new Date()));
        frontUserService.updateById(frontUser);
        return ApiResult.success(dict);
    }

    /**
     * 绑定用户的微信openid
     *
     * @return 新增结果
     * @author jzw
     * @since 2022-01-07 15:45:19
     */
    @PostMapping("bindPlayerWechart")
    public ApiResult<Map<String, Object>> bindPlayerWechart(@Validated @RequestBody BindWechartReq loginInfoReq, @RequestHeader(required = false, name = "Captcha-Token") String captchaToken) {
        Assert.notBlank(captchaToken, "captchaToken 不能为空");
        SaSession saSession = SaSessionCustomUtil.getSessionById(captchaToken, false);
        Assert.notNull(saSession, "验证码已失效");
        Object captchaSessionKey = saSession.get(Constant.CAPTCHA_SESSION_KEY);
        Assert.notNull(captchaSessionKey, "验证码已失效");
        Assert.isTrue(loginInfoReq.getCode().equalsIgnoreCase((String) captchaSessionKey), "验证码不正确");
        saSession.clear();
        LambdaQueryWrapper<FrontUser> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(FrontUser::getAccount, loginInfoReq.getAccount());
        FrontUser frontUser = frontUserService.getOne(wrapper);
        if (frontUser == null) {
            return ApiResult.fail("账户或密码错误");
        }
        String password = frontUser.getPassword();
        Assert.notBlank(password, "密码不能为空");
        boolean b = DigestUtil.bcryptCheck(loginInfoReq.getPassword(), password);
        if (b) {
            SaSession session = SaSessionCustomUtil.getSessionById(loginInfoReq.getToken(), false);
            if (session == null) {
                return ApiResult.fail("绑定超时或无效,请退出后重新进入该页面进行绑定");
            }
            else {
                String openId = session.getString("openId");
                String accessToken = session.getString("accessToken");
                long count = frontUserService.lambdaQuery().eq(FrontUser::getOpenId, openId).count();
                if (count > 0) {
                    return ApiResult.fail("该微信已经绑定了其他账号!");
                }
                if (StrUtil.isNotBlank(frontUser.getOpenId())) {
                    if (StrUtil.equalsIgnoreCase(frontUser.getOpenId(), openId)) {
                        return ApiResult.fail("当前账户已绑定该微信,请勿重复绑定!");
                    }
                    else {
                        return ApiResult.fail("当前账户已绑定其他微信,请在交易网pc端--个人中心,解绑后再进行绑定!");
                    }
                }
                else {
                    log.info("进行微信绑定操作:{}", loginInfoReq);
                    JSONObject wechartInfo = getWechartInfo(accessToken, openId);
                    // HttpUtil.
                    frontUserService.lambdaUpdate()
                            .set(FrontUser::getOpenId, openId)
                            .set(FrontUser::getAvatar, wechartInfo.getStr("headimgurl"))
                            .eq(FrontUser::getId, frontUser.getId())
                            .update();
                    session.clear();
                    return ApiResult.success();
                }
            }

        }
        return ApiResult.fail("账号或密码错误");
    }

    private JSONObject getWechartInfo(String token, String openId) {
        String params = "?access_token=" + token + "&openid=" + openId + "&lang=zh_CN";
        String url = Constants.WX_USERINFO_URL;
        String res = HttpUtil.get(url + params);
        log.info("通过请求获取 微信用户信息 : {}", res);
        return JSONUtil.parseObj(res);
    }

}
